package com.gujiangbo.lock;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;

/**
 * @Author: gujiangbo.co
 * @Contact: 646832012@qq.com
 * @Date: 2022/3/14 20:33
 * @Version: 1.0
 * @Description: LockSupport是用来创建锁和其他同步类的基本线程阻塞原语。
 * LockSupport是一个线程阻塞工具类，所有的方法都是静态方法，可以让线程在任意位置阻塞，阻塞之后也有对应的唤醒方法。
 * 归根结底，LockSupport调用的Unsafe中的native方法。
 * LockSupport提供park()和unpark()方法实现阻塞线程和接触线程阻塞的过程
 * LockSupport和每个使用它的线程都有一个许可(permit)关联。permit相当于1，0的开关，默认是0
 * 调用一次unpark就加1变成1
 * 调用一次park就会消费permit,也就是将1变成0，同时park立即返回。
 * 如果再次调用park会变成阻塞(因为permit为零了会阻塞在这里,一直到permit变为1),这时调用unpark会把permit设置为1
 * 每个线程都有一个相关的permit，permit最多只有一个，重复调用unpark也不会积累凭证。
 * <p>
 * 面试题：
 * 1、为什么可以先唤醒线程后阻塞线程？
 * 因为unpark获得一个凭证，之后在调用park方法，就可以名正言顺的消费，故不会阻塞
 * 2、为什么唤醒两次后阻塞两次，但最终结果还会阻塞线程？
 * 因为凭证的数量最多为1，连续调用两次unpark和调用一次unpark的效果是一样的，只会增加一个凭证，
 * 而调用两次park却需要消费两个凭证，凭证不够，所以不能放行。
 */
public class JavaLockSupport {

    public static void main(String[] args) {

        Thread t1 = new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "\t  coming in...");
            LockSupport.park();
            System.out.println(Thread.currentThread().getName() + "\t 被唤醒了");

        }, "A");
        t1.start();
        //暂停几秒钟线程
        try {
            TimeUnit.SECONDS.sleep(3L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        Thread t2 = new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "\t 开始唤醒....");
            LockSupport.unpark(t1);
            LockSupport.unpark(t1);
            LockSupport.unpark(t1);
            LockSupport.unpark(t1);// 多次unpark不会积累凭证，被允许多次调用
        }, "B");
        t2.start();


    }

}
